# -*-coding:utf-8 -*-
#
# Created on 2019/6/11, by felix
#
import json

from django.http import HttpResponse
from django_filters.rest_framework import DjangoFilterBackend
from collections import OrderedDict
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.generics import get_object_or_404
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import Response, APIView
from rest_framework.decorators import list_route, action
from django.utils import six
from django.core.paginator import InvalidPage
from rest_framework.exceptions import NotFound
from rest_framework.pagination import (_positive_int, PageNumberPagination)  # 简单分页


# 100 APIView
# 使用场景：不是该项目数据库中存在的数据
class APIBaseViewSet(APIView):
    json_content_type = {'content_type': 'application/json'}

    def render_to_json_response(self, status=0, msg=u'成功', data={}):
        context = {}
        context.update(status=status, msg=msg, data=data)

        return HttpResponse(json.dumps(context), content_type=self.json_content_type)


# 200 自定义分页器
# 使用场景：?page=2&page_size=1  返回指定数据  ?page_size=-1  返回全部数据
class CustomPageNumberPagination(PageNumberPagination):
    page_size_query_param = 'page_size'
    page_size = 15
    page_number = 1

    def paginate_queryset(self, queryset, request, view=None):
        self.page_size = self.get_page_size(request)

        # 重新定义page_size, 返回所有data，
        if self.page_size == -1:
            self.count = self.get_count(queryset)  # len(queryset) or queryset.count()
            return list(queryset)

        paginator = self.django_paginator_class(queryset, self.page_size)  # 分页器继承django的paginator

        self.page_number = request.query_params.get(self.page_query_param, 1)  # request.query_params.get('page')
        if self.page_number in self.last_page_strings:
            self.page_number = paginator.num_pages

        try:
            self.page = paginator.page(self.page_number)
        except InvalidPage as exc:
            msg = self.invalid_page_message.format(
                page_number=self.page_number, message=six.text_type(exc)
            )
            raise NotFound(msg)

        if paginator.num_pages > 1 and self.template is not None:
            # The browsable API should display pagination controls.#  可浏览的API，应该显示分页控件
            self.display_page_controls = True

        self.request = request
        return list(self.page)

    def get_paginated_response(self, data):
        if self.page_size == -1:  # page_size=1 重写定义
            return Response(OrderedDict([
                ('status', 0),
                ('count', self.count),  # queryset的全部总数
                ('page', self.page_number),
                ('page_size', self.page_size),
                ('data', data)
            ]))
        return Response(OrderedDict([
            ('status', 0),
            ('count', self.page.paginator.count),
            ('page', self.page_number),
            ('page_size', self.page_size),
            ('data', data)
        ]))

    def get_page_size(self, request):  # request.query_params.get('page_size')
        page_size = request.query_params[self.page_size_query_param] \
            if request.query_params.get(self.page_size_query_param) else self.page_size

        # 转换为int类型
        try:
            page_size = int(page_size)
        except Exception as e:
            page_size = self.page_size
        return page_size

    def get_count(self, queryset):
        """
        Determine an object count, supporting either querysets or regular lists.
        定一个对象计数，支持查询集或常规列表
        """
        try:
            return queryset.count()
        except (AttributeError, TypeError):
            return len(queryset)


# 300 ModelViewSet
# 使用场景：有数据库的Model
# 继承ModelViewSet,
class BaseViewSet(ModelViewSet):
    pagination_class = CustomPageNumberPagination  # 分页器
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)  # 过滤字段
    json_content_type = {'content_type': 'application/json'}  # 返回给前端的数据，转化为json

    # 001 获取单个对象object
    # 继承 GenericAPIView
    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
                'Expected view %s to be called with a URL keyword argument '
                'named "%s". Fix your URL conf, or set the `.lookup_field` '
                'attribute on the view correctly.' %
                (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    # 002 put更新单个
    # 继承mixins.UpdateModelMixin
    def update(self, request, *args, **kwargs):
        """
        put /entity/{pk}/
        """
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return self.render_to_json_response(data=serializer.data)

    # 003 delete删除单个
    # 继承mixins.DestroyModelMixin
    def destroy(self, request, *args, **kwargs):
        """
        delete /entity/{pk}/
        """
        instance = self.get_object()
        instance.is_deleted = True
        instance.save()
        return self.render_to_json_response()

    # 004 get检索单个
    # 继承mixins.RetrieveModelMixin
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return self.render_to_json_response(data=serializer.data)

    # 005 post创建单个
    # 继承 mixins.CreateModelMixin
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        return self.render_to_json_response(data=serializer.data)

    # 006 重新定义，给前端的返回值
    # mixins的默认返回用的是return Response(serializer.data)
    def render_to_json_response(self, status=0, msg=u'成功', data={}):
        # 错误提示
        context = {}
        context.update(status=status, msg=msg, data=data)

        return HttpResponse(json.dumps(context), content_type=self.json_content_type)

    # 007 get所有数据
    # 继承mixins.ListModelMixin
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        # 存在分页，?page=2&page_size=1  序列化分页的数据 page
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        # 否则，序列化全部数据 queryset
        serializer = self.get_serializer(queryset, many=True)
        return self.get_paginated_response(data=serializer.data)

    # 008 批量删除
    # delete 方法
    # body：{"ids":[10,11,12]}
    """
    方法1： /command/info/bulk-delete
        @action(methods=['delete'], url_path='bulk-delete', detail=False)
        def bulk_delete(self, request, *args, **kwargs):
    """

    # 方法2： /command/info
    def delete(self, request, *args, **kwargs):
        ids = request.data.get('ids', '')
        if not ids:
            return self.render_to_json_response(status=1, msg='请输入参数ids')
        try:
            self.get_queryset().filter(id__in=ids, is_deleted=False).update(is_deleted=True)
            return self.render_to_json_response(msg='批量删除成功')
        except Exception as e:
            return self.render_to_json_response(status=1, msg='批量删除失败%s' % e)
